// server.go
//
// 长连接回显服务器：
//   - 监听 127.0.0.1:9000
//   - 针对每条 TCP 连接启动 goroutine
//   - 使用 SetReadDeadline 结合心跳检测连接活跃
//   - 约定消息以 '\n' 结尾避免粘包问题
package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
	"time"
)

const (
	listenAddr      = "127.0.0.1:9101" // 服务器监听地址
	readIdleTimeout = 30 * time.Second // 超过此时间未读到数据则判定超时
	heartbeatReq    = "PING"           // 客户端心跳请求
	heartbeatResp   = "PONG"           // 服务器心跳响应
)

func main() {
	// 1. 启动 TCP 监听。若 IP 为空字符串则监听所有网卡，本示例限定回环地址。
	listener, err := net.Listen("tcp", listenAddr)
	if err != nil {
		log.Fatalf("listen error: %v", err)
	}
	log.Printf("长连接服务器已启动，监听 %s ...", listenAddr)

	for {
		// 2. Accept 阻塞等待客户端完成三次握手并返回连接。
		conn, err := listener.Accept()
		if err != nil {
			log.Printf("accept error: %v", err)
			continue
		}

		log.Printf("新连接 remote=%s local=%s",
			conn.RemoteAddr(), conn.LocalAddr())

		// 3. 每个连接交由独立 goroutine 处理，充分利用 Go 的并发模型。
		go handleConn(conn)
	}
}

// handleConn 管理单个长连接的读写与超时。
func handleConn(conn net.Conn) {
	defer func() {
		log.Printf("连接关闭 remote=%s", conn.RemoteAddr())
		conn.Close()
	}()

	reader := bufio.NewScanner(conn)

	for {
		// 4. 每次读取前刷新读超时，防止“静默”连接占用资源。
		if err := conn.SetReadDeadline(time.Now().Add(readIdleTimeout)); err != nil {
			log.Printf("set deadline error: %v", err)
			return
		}

		// 5. Scanner 按行读取数据；遇到超时或 EOF 将返回 false。
		if !reader.Scan() {
			if err := reader.Err(); err != nil {
				log.Printf("read error from %s: %v", conn.RemoteAddr(), err)
			} else {
				log.Printf("对端主动关闭：%s", conn.RemoteAddr())
			}
			return
		}

		msg := strings.TrimSpace(reader.Text())

		// 6. 心跳请求：收到 PING 就回复 PONG，并继续等待下一条消息。
		if msg == heartbeatReq {
			fmt.Fprintf(conn, "%s\n", heartbeatResp)
			continue
		}

		// 7. 业务处理：此处简单记录日志并回显，可扩展为鉴权、推送等。
		log.Printf("业务消息来自 %s => %q", conn.RemoteAddr(), msg)
		if _, err := fmt.Fprintf(conn, "echo: %s\n", msg); err != nil {
			log.Printf("write error: %v", err)
			return
		}
	}
}
